// ========================================================================
// Copyright (c) 2022 ChangYou, All rights reserved.
// http://www.changyou.com/
// 
// Filename:    RenderingQueueInstance.cs
// Time:        2022-08-22 21:10:25
// Author:      xiangjinbao
// Email:       xiangjinbao@cyou-inc.com
// Version:     v2022.0.1
// Description: RenderingQueueInstance
// ========================================================================

using System.Collections.Generic;
using UnityEngine;

namespace ProjectTW.Foliage
{
    /// <summary>
    /// An rendering queue instance.
    /// </summary>
    public class RenderingQueueInstance
    {
        public FoliageInstanceObject Instance;
        public FoliagePrototype Prototype;
        
        private FoliageGPUMesh m_FoliageGpuMesh;
        private byte m_MaxPrototypeDensity;

        // 渲染队列模型实例模拟器
        public UNFastList<RenderingQueueMeshInstanceSimulator> SimulatedMeshInstances;
        // GPU实例堆栈
        public Dictionary<Mesh, GPUInstancingStack> GPUInstancingStash;

        public RenderingQueueInstance(FoliageInstanceObject instance, FoliagePrototype prototype, FoliageGPUMesh foliageGpuMesh, FoliageDensityMap densityMap)
        {
            this.Instance = instance;
            this.Prototype = prototype;
            this.m_FoliageGpuMesh = foliageGpuMesh;
            
            m_MaxPrototypeDensity = prototype.m_MaxGeneratedDensity;
            SimulatedMeshInstances = null;

            // 渲染对象实例持有实例堆栈
            if (prototype.UseInstancing)
            {
                GPUInstancingStash = GPUInstancingCore.CreateInstancingStack(foliageGpuMesh); // update instancing stuck.
            }
        }

        internal void UpdateDensities(int mapWidth, float densityMultiplier)
        {
            int simulatedMeshInstancesCount = SimulatedMeshInstances.Count;

            for (int i = 0; i < simulatedMeshInstancesCount; i++)
            {
                SimulatedMeshInstances.arrayAllocation[i].UpdateDensity(m_FoliageGpuMesh, m_MaxPrototypeDensity, Instance, mapWidth, densityMultiplier, Prototype, GPUInstancingStash);
            }
        }

        public void Render(MaterialPropertyBlock propertyBlock, Camera camera, Plane[] cameraPlanes, Vector3 normalizedCameraPosition, bool instancingEnabled)
        {
            propertyBlock.SetTexture(FoliageMeshManager.PropertyIdDensitymap, Instance.DensityMap.Map);
            propertyBlock.SetFloat(FoliageMeshManager.PropertyIdPrototype, Prototype.Id);

            if (SimulatedMeshInstances == null)
            {
                return;
            }

            if (instancingEnabled)
            {
                GPUInstancingCore.ResetInstancingStack(GPUInstancingStash, m_FoliageGpuMesh);
            }

            for (int i = 0; i < SimulatedMeshInstances.Count; i++)
            {
                SimulatedMeshInstances.arrayAllocation[i].Render(propertyBlock, camera, cameraPlanes, normalizedCameraPosition, instancingEnabled);
            }

            if (instancingEnabled)
            {
                GPUInstancingCore.RenderInstancingStack(GPUInstancingStash, m_FoliageGpuMesh, Prototype, propertyBlock, camera);
            }
        }
    }
}
